/*
 * Copyright 2009 Alberto Gimeno <gimenete at gmail.com>
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package siena.remote;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;

import siena.ClassInfo;
import siena.IterableQuery;
import siena.Model;
import siena.Query;
import siena.SienaException;
import siena.Util;

public class RemoteQuery<T> implements Query<T> {
	
	private Document request;
	private RemotePersistenceManager pm;
	private Class<?> clazz;
	
	public RemoteQuery(RemotePersistenceManager pm, Class<?> clazz) {
		this.clazz = clazz;
		this.pm = pm;
		request = pm.createRequest("query");
		request.getRootElement().addAttribute("class", clazz.getName());
	}

	@SuppressWarnings("unchecked")
	public List<T> fetch() {
		Document response = pm.send(request);
		Element root = response.getRootElement();
		if("error".equals(root.getName())) {
			throw new SienaException(root.attributeValue("class") + " " +root.getText());
		}
		List<Element> result = response.getRootElement().elements("object");
		List<T> list = new ArrayList<T>(result.size());
		for (Element element : result) {
			list.add((T) Common.parseEntity(element, null));
		}
		// TODO get nextOffset in response
		return list;
	}

	public List<T> fetch(int limit) {
		request.getRootElement().addAttribute("limit", Integer.toString(limit));
		return fetch();
	}

	public List<T> fetch(int limit, Object offset) {
		request.getRootElement().addAttribute("limit",  Integer.toString(limit));
		request.getRootElement().addAttribute("offset", offset.toString());
		return fetch();
	}

	public Query<T> filter(String fieldName, Object value) {
		Element filter = request.getRootElement().addElement("filter");
		filter.addAttribute("field", fieldName);
		Field f = null;
		try {
			f = clazz.getDeclaredField(fieldName);
		} catch (Exception e) {
			throw new SienaException(e);
		}
		if(value != null) {
			if(ClassInfo.isModel(value.getClass())) {
				Common.fillRequestElement((Model) value, filter, true);
			} else {
				filter.setText(Util.toString(f, value));
			}
		}
		return this;
	}

	public T get() {
		List<T> list = fetch(1);
		if(list.isEmpty()) return null;
		return list.get(0);
	}

	public Query<T> order(String fieldName) {
		request.getRootElement().addElement("order").addAttribute("field", fieldName);
		return this;
	}

	public int count() {
		return fetch().size(); // TODO: change this!
	}

	public int count(int limit) {
		return fetch(limit).size(); // TODO: change this!
	}

	public int count(int limit, Object offset) {
		return fetch(limit, offset).size(); // TODO: change this!
	}

	public Iterable<T> iter(String field, int max) {
		return new IterableQuery<T>(this, max, field);
	}

	public Query<T> search(String match, boolean inBooleanMode,
			String... fieldNames) {
		throw new UnsupportedOperationException();
	}
	
	public RemoteQuery<T> clone() {
		RemoteQuery<T> clone = new RemoteQuery<T>(pm, clazz);
		clone.request = (Document) request.clone();
		return clone;
	}
	
	@Override
	public Object nextOffset() {
		return null; // TODO: get nextOffset in response document
	}

	@Override
	public int delete() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public List<T> fetchKeys() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<T> fetchKeys(int limit) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<T> fetchKeys(int limit, Object offset) {
		// TODO Auto-generated method stub
		return null;
	}

}
